package com.culture.bootdemo.controller.thread;

import com.culture.bootdemo.common.exception.BusinessException;
import com.culture.bootdemo.dao.AnswerMapper;
import com.culture.bootdemo.po.Answer;
import com.culture.bootdemo.service.AnswerService;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;


/**
 * 线程事务
 */
@RestController
@RequestMapping("/threadTransaction")
@Slf4j
public class ThreadTransactionController {

    @Autowired
    private AnswerService answerService;
    @Autowired
    public AnswerMapper answerMapper;

    private DataSourceTransactionManager txManager;

    private DefaultTransactionDefinition transactionDefinition;

    private static final ExecutorService EXECUTOR = new ThreadPoolExecutor(8, 9, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build());

    @Autowired
    public void setTxManager(DataSourceTransactionManager txManager) {
        this.txManager = txManager;
    }

    @Autowired
    public void setTransactionDefinition(DefaultTransactionDefinition transactionDefinition) {
        transactionDefinition.setIsolationLevel(DefaultTransactionDefinition.ISOLATION_READ_COMMITTED);
        transactionDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW);
        this.transactionDefinition = transactionDefinition;
    }

    /**
     * 部分回滚：多线程抛异常，内部回滚，主线程不会滚
     * spring事务：需要借助aop,所以两个事务需要在两个service中
     */
    @GetMapping("test1")
    public void test1() throws Exception {
        answerService.addtest();
    }


    @GetMapping("test2")
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void test2() {
        answerMapper.insert(new Answer(3, "cccc"));
        Thread thread = new Thread(() -> {
            answerMapper.insert(new Answer(4, "dddd"));
            int i = 1 / 0;
        });
        thread.start();
    }


    /**
     * 部分回滚：主线程不回滚
     */
    @GetMapping("test3")
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void test3() {
        answerMapper.insert(new Answer(3, "cccc"));
        Thread thread = new Thread(() -> {
            answerService.addtest();
        });
        thread.start();
    }

    /**
     * 部分回滚：主线程不回滚
     */
    @GetMapping("test4")
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void test4() {
        answerMapper.insert(new Answer(3, "cccc"));
        EXECUTOR.execute(() -> {
            answerService.addtest();
        });
    }

    /**
     * 全部回滚
     *
     * @throws Exception
     */
    @GetMapping("test5")
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void test5() throws Exception {
        boolean a = false;
        answerMapper.insert(new Answer(3, "cccc"));
        //Callable 结合 FutureTask实现==
        CallableInsertTask task = new CallableInsertTask();
        FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
        new Thread(integerFutureTask).start();
        try {
            System.out.println("task运行结果：" + integerFutureTask.get());
        } catch (Exception e) {
            throw new BusinessException("重置");
        }

    }

    class CallableInsertTask implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            answerService.addtest();
            return new Random().nextInt();
        }
    }


    /**
     * 一个taskId下的逻辑有错误，这个taskId涉及的表数据全部回滚，不影响之前正确的taskId
     */
    @GetMapping("test45")
    public void test45() {
        try {
            List<Integer> taskIdList = Arrays.asList(1, 2, 3);
            for (Integer taskId : taskIdList) {
                answerService.testDate(taskId);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("6666666666666666666");
        }
    }

}
